package com.kjlink.widget.oa.helper;

import com.kjlink.widget.oa.enums.OaApiUrlInterface;
import org.apache.commons.codec.digest.DigestUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.http.*;
import org.springframework.http.client.ClientHttpRequestExecution;
import org.springframework.http.client.ClientHttpRequestInterceptor;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.util.Assert;
import org.springframework.util.StreamUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.util.UriComponentsBuilder;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.Collections;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 描述:
 * oa 请求帮助类
 *
 * @author Vic.xu
 * @since 2025-09-01 9:26
 */
public class OaApiHelper {

    private static final Logger LOGGER = LoggerFactory.getLogger(OaApiHelper.class);

    private String oaHost = "http://192.168.80.58:8087";

    private String appId = "newoa";

    private String secureKey = "gdG9rZW4iLCJhdWQiOiJBUFAiLCJuYmYiOjE3";

    private static final DateTimeFormatter LAST_REQUEST_TIME_FORMAT = DateTimeFormatter.ofPattern("yyyy-MM-dd");

    /**
     * 最后请求时间
     */
    private static final Map<String, String> LAST_REQUEST_TIME = new ConcurrentHashMap<>();

    static RestTemplate restTemplate = new RestTemplate();

    static {
        restTemplate.setInterceptors(
                Collections.singletonList(new ResponseLoggingInterceptor())
        );
    }

    /**
     * 同步 成功 则保留最后一次请求时间
     *
     * @param urlEnum
     */
    public void successSyncData(OaApiUrlInterface urlEnum) {
        LAST_REQUEST_TIME.put(urlEnum.getUrl(), LocalDate.now().format(LAST_REQUEST_TIME_FORMAT));
    }

    /**
     * 从oa同步数据 增量方式, 当不存在最后请求时间 则是全量
     */
    public <T> T syncFromOa(OaApiUrlInterface urlEnum) {
        return syncFromOa(urlEnum, true);
    }

    /**
     * 从oa同步数据
     *
     * @param urlEnum 类型
     * @param full    是否全量
     */
    public <T> T syncFromOa(OaApiUrlInterface urlEnum, boolean full) {
        String url = getUrlWithCommonParams(urlEnum, full);
        LOGGER.info("开始从oa 进行 [{}] 数据同步操作, url = ", urlEnum.getName(), url);
        HttpEntity<?> requestEntity = getHttpEntity(urlEnum);
        //  从枚举中获取动态返回类型
        ParameterizedTypeReference<T> responseType =
                (ParameterizedTypeReference<T>) urlEnum.getResponseType();

        ResponseEntity<T> response = restTemplate.exchange(
                url,
                urlEnum.getMethod(),
                requestEntity,
                responseType
        );
        return response.getBody();
    }

    /**
     * 获取HttpEntity:  主要是为了设置请求头
     */
    private HttpEntity<?> getHttpEntity(OaApiUrlInterface urlEnum) {
        // 设置请求头
        HttpHeaders headers = new HttpHeaders();
        headers.add("Access-Control-Request-Headers", "oa");
        headers.set("Accept", MediaType.APPLICATION_JSON_VALUE);
        headers.add("appId", appId);
        headers.add("Origin", "oa");
        return new HttpEntity<>(headers);
    }

    /**
     * 获取URL
     * 拼接host  和最后请求时间(如果有必要的话)
     */
    private String getUrl(OaApiUrlInterface urlEnum, boolean full) {
        Assert.notNull(urlEnum, "urlEnum can not be null");
        String url = oaHost + urlEnum.getUrl();
        if (!full) {
            String lastRequestTime = LAST_REQUEST_TIME.get(urlEnum.getUrl());
            if (StringUtils.hasText(lastRequestTime)) {
                url += "/" + lastRequestTime;
            }
        }
        return url;
    }

    /**
     * 获取带参数的URL
     */
    private String getUrlWithCommonParams(OaApiUrlInterface urlEnum, boolean full) {
        String url = getUrl(urlEnum, full);
        // 1. 构建带查询参数的URL
        UriComponentsBuilder uriBuilder = UriComponentsBuilder.fromHttpUrl(url);
        long timeMillis = System.currentTimeMillis();
        String timestamp = String.valueOf(timeMillis);
        String sign = signStr(timeMillis);
        // 遍历参数并添加到URL
        uriBuilder.queryParam("timestamp", timestamp);
        uriBuilder.queryParam("signStr", sign);

        return uriBuilder.toUriString();
    }


    /**
     * 签名
     *
     * @param timestamp
     */
    public String signStr(long timestamp) {
        String str = appId + secureKey + timestamp;
        return DigestUtils.md5Hex(str);
    }


    public static Map<String, String> getLastRequestTimeMap(){
        return LAST_REQUEST_TIME;
    }



    // 1. 自定义拦截器记录原始响应体
    public static class ResponseLoggingInterceptor implements ClientHttpRequestInterceptor {
        @Override
        public ClientHttpResponse intercept(
                HttpRequest request,
                byte[] body,
                ClientHttpRequestExecution execution
        ) throws IOException {
            // 执行请求并获取响应
            ClientHttpResponse response = execution.execute(request, body);

            // 读取原始响应体（注意：需要包装响应流，否则后续解析会报错）
            String rawResponseBody = StreamUtils.copyToString(
                    response.getBody(),
                    StandardCharsets.UTF_8
            );
            LOGGER.debug("{}原始响应体:{} \n\t", request.getURI().getPath(), rawResponseBody); // 记录原始响应

            // 返回包装后的响应流（允许后续 RestTemplate 再次读取）
            return new CachedClientHttpResponse(response, rawResponseBody);
        }
    }

    // 2. 包装响应流的工具类（避免流被读取一次后关闭）
    static class CachedClientHttpResponse implements ClientHttpResponse {

        private final ClientHttpResponse originalResponse;
        private final byte[] cachedBody;

        /**
         * 构造器：缓存原始响应体
         *
         * @param originalResponse 原始响应对象
         * @param rawResponseBody  原始响应体字符串
         */
        public CachedClientHttpResponse(ClientHttpResponse originalResponse, String rawResponseBody) {
            this.originalResponse = originalResponse;
            this.cachedBody = rawResponseBody.getBytes(StandardCharsets.UTF_8);
        }

        /**
         * 返回缓存的响应体流（可多次读取）
         */
        @Override
        public InputStream getBody() throws IOException {
            return new ByteArrayInputStream(cachedBody);
        }

        /**
         * 委托原始响应的响应头
         */
        @Override
        public HttpHeaders getHeaders() {
            return originalResponse.getHeaders();
        }

        /**
         * 委托原始响应的状态码
         */
        @Override
        public HttpStatus getStatusCode() throws IOException {
            return originalResponse.getStatusCode();
        }

        /**
         * 委托原始响应的状态码（int类型）
         */
        @Override
        public int getRawStatusCode() throws IOException {
            return originalResponse.getRawStatusCode();
        }

        /**
         * 委托原始响应的状态文本
         */
        @Override
        public String getStatusText() throws IOException {
            return originalResponse.getStatusText();
        }

        /**
         * 关闭原始响应的资源（缓存的流无需单独关闭）
         */
        @Override
        public void close() {
            originalResponse.close();
        }
    }

}
